/**h* DesfireAPI/Ciphering
 *
 * NAME
 *   DesfireAPI :: Ciphering module
 *
 * COPYRIGHT
 *   (c) 2009 SpringCard - www.springcard.com
 *
 * DESCRIPTION
 *   Algorithm independant implementation of the DESFIRE ciphering schemes.
 *
 **/
#include "sprox_desfire_i.h"

void Desfire_InitCrypto3Des(SPROX_PARAM  const BYTE des_key1[8], const BYTE des_key2[8], const BYTE des_key3[8])
{
  SPROX_DESFIRE_GET_CTX_V();

  if (des_key1 == NULL) return;
  if (des_key2 == NULL) des_key2 = des_key1;
  if (des_key3 == NULL) des_key3 = des_key1;

  TDES_Init(&ctx->cipher_context.tdes, des_key1, des_key2, des_key3);
}

void Desfire_InitCryptoAes(SPROX_PARAM  const BYTE aes_key[16])
{
  SPROX_DESFIRE_GET_CTX_V();

  if (aes_key == NULL) return;

  AES_Init(&ctx->cipher_context.aes, aes_key);
}

void Desfire_CipherRecv(SPROX_PARAM  BYTE data[], DWORD *length)
{
  BYTE buffer[16];
  WORD  cipher_blocks;
  DWORD i, j;
  SPROX_DESFIRE_GET_CTX_V();

  if ((data == NULL) || (length == NULL))
    return;

  switch (ctx->session_key_type)
  {
    case KEY_LEGACY_DES : 
      {
        cipher_blocks = (WORD) (*length / 8);

        memset(buffer, 0x00, 8);      /* B  <- 00...00   */
        for (i = 0; i < cipher_blocks; i++)
        {
          memcpy(ctx->init_vector, buffer, 8);   /* IV <- B         */
          memcpy(buffer, &data[8 * i], 8);  /* B  <- P         */
          TDES_Decrypt2(&ctx->cipher_context.tdes, &data[8 * i], buffer); /* P  <- iDES(B)   */
          for (j = 0; j < 8; j++)
            data[8 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
        }
      }
      break;

    case KEY_LEGACY_3DES :
      {
        cipher_blocks = (WORD) (*length / 8);

        memset(buffer, 0x00, 8);      /* B  <- 00...00   */
        for (i = 0; i < cipher_blocks; i++)
        {
          memcpy(ctx->init_vector, buffer, 8);   /* IV <- B         */
          memcpy(buffer, &data[8 * i], 8);  /* B  <- P         */
          TDES_Decrypt2(&ctx->cipher_context.tdes, &data[8 * i], buffer); /* P  <- iDES(B)   */
          for (j = 0; j < 8; j++)
            data[8 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
        }
      }
      break;

    case KEY_ISO_DES    :
    case KEY_ISO_3DES2K :
    case KEY_ISO_3DES3K :
      {
        cipher_blocks = (WORD) (*length / 8);
 
        for (i = 0; i < cipher_blocks; i++)
        {
          memcpy(buffer, &data[8 * i], 8);  /* B  <- P         */
          TDES_Decrypt2(&ctx->cipher_context.tdes, &data[8 * i], buffer); /* P  <- iDES(B)   */
          for (j = 0; j < 8; j++)
            data[8 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
          memcpy(ctx->init_vector, buffer, 8); /* IV <- B         */
        }

      }
      break;

    case KEY_ISO_AES :
      {
        cipher_blocks = (WORD) (*length / 16);
 
        for (i = 0; i < cipher_blocks; i++)
        {
          memcpy(buffer, &data[16 * i], 16);  /* B  <- P         */
          AES_Decrypt2(&ctx->cipher_context.aes, &data[16 * i], buffer); /* P  <- iDES(B)   */
          for (j = 0; j < 16; j++)
            data[16 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
          memcpy(ctx->init_vector, buffer, 16); /* IV <- B         */
        }

      }
      break;
  }
}

void Desfire_CipherSend(SPROX_PARAM  BYTE data[], DWORD *length, DWORD max_length)
{
  DWORD actual_length;
  DWORD block_size;
  DWORD block_count;
  DWORD i, j;
  SPROX_DESFIRE_GET_CTX_V();

  if ((data == NULL) || (length == NULL))
    return;

  actual_length = *length;

  /* Step 1 : padding */
  block_size = (ctx->session_key_type == KEY_ISO_AES) ? 16 : 8;
  while (actual_length % block_size)
  {
    if (actual_length >= max_length)
      return;
    data[actual_length++] = 0x00;
  }

  block_count = (actual_length / block_size);

  switch (ctx->session_key_type)
  {
    case KEY_LEGACY_DES : 
      {
        /* IV <- 0 */
        memset(ctx->init_vector, 0, sizeof(ctx->init_vector));
        for (i = 0; i < block_count; i++)
        {
          for (j = 0; j < 8; j++)
            data[8 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
          /* Legacy mode : PICC always encrypts, PCD always decrypts */
          TDES_Decrypt2(&ctx->cipher_context.tdes, ctx->init_vector, &data[8 * i]);  /* IV <- i3DES(P)   */
          memcpy(&data[8 * i], ctx->init_vector, 8); /* P  <- IV        */
        }
      }
      break;

    case KEY_LEGACY_3DES :
      {
        /* IV <- 0 */
        memset(ctx->init_vector, 0, sizeof(ctx->init_vector));
        for (i = 0; i < block_count; i++)
        {
          for (j = 0; j < 8; j++)
            data[8 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
          /* Legacy mode : PICC always encrypts, PCD always decrypts */
          TDES_Decrypt2(&ctx->cipher_context.tdes, ctx->init_vector, &data[8 * i]);  /* IV <- i3DES(P)   */
          memcpy(&data[8 * i], ctx->init_vector, 8); /* P  <- IV        */
        }
      }
      break;

    case KEY_ISO_DES    :
    case KEY_ISO_3DES2K :
    case KEY_ISO_3DES3K :
      {
        /* Keep last IV */
        for (i = 0; i < block_count; i++)
        {
          for (j = 0; j < 8; j++)
            data[8 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
          /* ISO mode : sending means encrypting */
          TDES_Encrypt2(&ctx->cipher_context.tdes, ctx->init_vector, &data[8 * i]);  /* IV <- 3DES(P)   */
          memcpy(&data[8 * i], ctx->init_vector, 8); /* P  <- IV        */
        }
      }
      break;

    case KEY_ISO_AES :
      {
        /* Keep last IV */
        for (i = 0; i < block_count; i++)
        {
          for (j = 0; j < 16; j++)
            data[16 * i + j] ^= ctx->init_vector[j];  /* P  <- P XOR IV  */
          /* ISO mode : sending means encrypting */
          AES_Encrypt2(&ctx->cipher_context.aes, ctx->init_vector, &data[16 * i]);  /* IV <- 3DES(P)   */
          memcpy(&data[16 * i], ctx->init_vector, 16); /* P  <- IV        */
        }
      }
      break;
  }

  *length = actual_length;
}

void Desfire_XferCipherSend(SPROX_PARAM  DWORD start_offset)
{
  DWORD block_size;
  DWORD actual_length;
  SPROX_DESFIRE_GET_CTX_V();

  if (start_offset > ctx->xfer_length)
    return;

  block_size = (ctx->session_key_type == KEY_ISO_AES) ? 16 : 8;

  /* Padd until we are aligned on block boundary */
  actual_length = ctx->xfer_length - start_offset;
  while (actual_length % block_size)
  {
    if (ctx->xfer_length >= sizeof(ctx->xfer_buffer))
      return;

    ctx->xfer_buffer[ctx->xfer_length++] = 0x00;
    actual_length++;
  }

  /* Cipher the data */
  Desfire_CipherSend(SPROX_PARAM_P  &ctx->xfer_buffer[start_offset], &actual_length, actual_length);
}

